#pragma once
#include "dll_export.h"
#include "LuaInfo.h"
#include "sysinfo.h"
#include <ostream>
#include <vector>
#include <boost/cstdint.hpp>
#include <boost/function.hpp>

namespace LuaDebug {

    class LogData;
    class Source;
    class Breakpoint;
    class BreakpointList;

    /// Internal type of the command data impl.
    typedef std::vector<char> data_container_t;

    /**
    * @brief Type of the command using TCP connection.
    */
    enum RemoteCommandType {
        REMOTECOMMANDTYPE_START_CONNECTION,
        REMOTECOMMANDTYPE_END_CONNECTION,

        REMOTECOMMANDTYPE_CHANGED_STATE,
        REMOTECOMMANDTYPE_UPDATE_SOURCE,
        REMOTECOMMANDTYPE_FORCE_UPDATESOURCE,
        REMOTECOMMANDTYPE_ADDED_SOURCE,
        REMOTECOMMANDTYPE_SAVE_SOURCE,
        REMOTECOMMANDTYPE_SET_UPDATECOUNT,

        REMOTECOMMANDTYPE_SET_BREAKPOINT,
        REMOTECOMMANDTYPE_REMOVE_BREAKPOINT,
        REMOTECOMMANDTYPE_CHANGED_BREAKPOINTLIST,

        REMOTECOMMANDTYPE_START,
        REMOTECOMMANDTYPE_END,
        REMOTECOMMANDTYPE_STEPINTO,
        REMOTECOMMANDTYPE_STEPOVER,
        REMOTECOMMANDTYPE_STEPRETURN,
        REMOTECOMMANDTYPE_BREAK,
        REMOTECOMMANDTYPE_RESUME,

        REMOTECOMMANDTYPE_SET_ENCODING,
        REMOTECOMMANDTYPE_OUTPUT_LOG,

        REMOTECOMMANDTYPE_EVALS_TO_VARLIST,
        REMOTECOMMANDTYPE_EVAL_TO_MULTIVAR,
        REMOTECOMMANDTYPE_EVAL_TO_VAR,

        REMOTECOMMANDTYPE_REQUEST_FIELDSVARLIST,
        REMOTECOMMANDTYPE_REQUEST_LOCALVARLIST,
        REMOTECOMMANDTYPE_REQUEST_GLOBALVARLIST,
        REMOTECOMMANDTYPE_REQUEST_REGISTRYVARLIST,
        REMOTECOMMANDTYPE_REQUEST_STACKLIST,
        REMOTECOMMANDTYPE_REQUEST_SOURCE,
        REMOTECOMMANDTYPE_REQUEST_BACKTRACELIST,

        REMOTECOMMANDTYPE_SUCCESSED,
        REMOTECOMMANDTYPE_FAILED,
        REMOTECOMMANDTYPE_VALUE_STRING,
        REMOTECOMMANDTYPE_VALUE_SOURCE,
        REMOTECOMMANDTYPE_VALUE_BREAKPOINTLIST,
        REMOTECOMMANDTYPE_VALUE_VARLIST,
        REMOTECOMMANDTYPE_VALUE_VAR,
        REMOTECOMMANDTYPE_VALUE_BACKTRACELIST,
    };

    /**
    * @brief The header of the command using TCP connection.
    */
    struct CommandHeader {
        union {
            RemoteCommandType type;
            boost::uint32_t dummy;
        } u;
        boost::uint32_t commandId;
        boost::uint32_t dataSize;

        explicit CommandHeader(void){}
        explicit CommandHeader(long type,long id){
            u.type = (RemoteCommandType)type;
            commandId = id;
        }
    };

    /// Check the CommandHeader size.
    BOOST_STATIC_ASSERT(sizeof(CommandHeader) == 4 * 3);

    /**
    * @brief Data type for command contents.
    */
    class _LUAVM_API CommandData
    {
        data_container_t m_data;

    public:
        explicit CommandData();
        explicit CommandData(const data_container_t &data);
        ~CommandData();

        /// Get the size of this data.
        data_container_t::size_type GetSize() const;

        /// Get the impl data of command.
        data_container_t &GetImplData();

        /// Get the impl data of command.
        const data_container_t &GetImplData() const;

        /// Get string for debug.
        std::string ToString() const;

    public:
        void Get_ChangedState(bool &isBreak) const;
        void Set_ChangedState(bool isBreak);

        void Get_UpdateSource(std::string &key, int &line, int &updateCount, bool &isRefreshOnly) const;
        void Set_UpdateSource(const std::string &key, int line, int updateCount, bool isRefreshOnly);

        void Get_AddedSource(Source &source) const;
        void Set_AddedSource(const Source &source);

        void Get_SaveSource(std::string &key, string_array &sources) const;
        void Set_SaveSource(const std::string &key, const string_array &sources);

        void Get_SetUpdateCount(int &updateCount) const;
        void Set_SetUpdateCount(int updateCount);

        void Get_SetBreakpoint(Breakpoint &bp) const;
        void Set_SetBreakpoint(const Breakpoint &bp);

        void Get_RemoveBreakpoint(Breakpoint &bp) const;
        void Set_RemoveBreakpoint(const Breakpoint &bp);

        void Get_ChangedBreakpointList(BreakpointList &bps) const;
        void Set_ChangedBreakpointList(const BreakpointList &bps);

        //void Get_SetEncoding(lldebug_Encoding &encoding) const;
        //void Set_SetEncoding(lldebug_Encoding encoding);

        void Get_OutputLog(LogData &logData) const;
        void Set_OutputLog(const LogData &logData);

        void Get_EvalsToVarList(string_array &evals, LuaStackFrame &stackFrame) const;
        void Set_EvalsToVarList(const string_array &evals, const LuaStackFrame &stackFrame);

        void Get_EvalToMultiVar(std::string &eval, LuaStackFrame &stackFrame) const;
        void Set_EvalToMultiVar(const std::string &eval, const LuaStackFrame &stackFrame);

        void Get_EvalToVar(std::string &eval, LuaStackFrame &stackFrame) const;
        void Set_EvalToVar(const std::string &eval, const LuaStackFrame &stackFrame);

        void Get_RequestFieldVarList(LuaVar &var) const;
        void Set_RequestFieldVarList(const LuaVar &var);

        void Get_RequestLocalVarList(LuaStackFrame &stackFrame, bool &checkLocal, bool &checkUpvalue, bool &checkEnviron) const;
        void Set_RequestLocalVarList(const LuaStackFrame &stackFrame, bool checkLocal, bool checkUpvalue, bool checkEnviron);

        void Get_RequestSource(std::string &key);
        void Set_RequestSource(const std::string &key);

        void Get_ValueString(std::string &str) const;
        void Set_ValueString(const std::string &str);

        void Get_ValueSource(Source &source) const;
        void Set_ValueSource(const Source &source);

        void Get_ValueVarList(LuaVarList &vars) const;
        void Set_ValueVarList(const LuaVarList &vars);

        void Get_ValueVar(LuaVar &var) const;
        void Set_ValueVar(const LuaVar &var);

        void Get_ValueBacktraceList(LuaBacktraceList &backtraces) const;
        void Set_ValueBacktraceList(const LuaBacktraceList &backtraces);
    };

    class _LUAVM_API LuaCommand
    {
    public:
        typedef boost::function<int(const LuaCommand &)> CommandCallback;

    private:
        CommandHeader   m_header;
        CommandData     m_data;
        CommandCallback m_response;

    public:
        explicit LuaCommand(void);
        explicit LuaCommand(const CommandHeader &header, const CommandData &data);
        ~LuaCommand(void);

        /// Get the type of this command.
        RemoteCommandType GetType() const {
            return m_header.u.type;
        }

        std::string GetTypeName() const;

        /// Get the command id.
        boost::uint32_t GetCommandId() const {
            return m_header.commandId;
        }

        /// Get the size of this data.
        boost::uint32_t GetDataSize() const {
            return m_header.dataSize;
        }

        /// Get the header of this command.
        CommandHeader &GetHeader() {
            return m_header;
        }

        /// Get the const header of this command.
        const CommandHeader &GetHeader() const {
            return m_header;
        }

        /// Get the const data of the command.
        CommandData &GetData() {
            return m_data;
        }

        /// Get the data of the command.
        const CommandData &GetData() const {
            return m_data;
        }

        /// Get the const impl data of the command data.
        std::vector<char> &GetImplData() {
            return m_data.GetImplData();
        }

        /// Get the const impl data of the command data.
        const std::vector<char> &GetImplData() const {
            return m_data.GetImplData();
        }

        /// Is this a response command ?
        bool IsResponse() const {
            return (m_response != NULL);
        }

        /// Set the response callback.
        void SetResponse(const CommandCallback &response) {
            m_response = response;
        }

        /// Call response function.
        void CallResponse() {
            m_response(*this);
            m_response.clear();
        }

        /// Get string for debug.
        std::string ToString() const {
            return m_data.ToString();
        }
    };

    /// Output the command data.
    template<class Ch, class Tr>
    std::basic_ostream<Ch,Tr> &operator << (std::basic_ostream<Ch,Tr> &os, const LuaCommand &command) {
        os << "type:      " << command.GetType() << std::endl;
        os << "commandId: " << command.GetCommandId() << std::endl;
        os << "datasize:  " << command.GetDataSize() << std::endl;
        if (command.GetDataSize() != 0) {
            os << "data:" << std::endl;
            os << command.ToString() << std::endl;
        }
        return os;
    }

} // namespace LuaDebug
